﻿using Bilibili.Common;
using Bilibili.Common.Api;
using Bilibili.Common.Database;
using Bilibili.Common.Log;
using Bilibili.Model.Common;
using Bilibili.Model.Resource;
using PCLUntils.Json;
using PCLUntils.Objects;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace Bilibili.WebClient.Common
{
    public sealed class HttpClientEx : IApiClient
    {
        private HttpClient httpClient;
        private CookieContainer cookieContainer;
        private HttpClientHandler clientHandler;
        public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(8);
        internal HttpClientEx()
        {
            UpdateClient();
            httpClient.DefaultRequestHeaders.Set(new Dictionary<string, string>
            {
                { "app-key", "android64" },
                { "Referrer", Current.HOST },
                { "x-bili-aurora-zone", "sh001" },
                { "x-bili-aurora-eid", "UlMFQVcABlAH" },
                { "UserAgent", Current.API_USER_AGENT }
            });
            httpClient.DefaultRequestHeaders.Referrer = new Uri(Current.HOST);
            httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(Current.API_USER_AGENT);
        }
        public void UpdateClient()
        {
            Dispose();
            clientHandler = new HttpClientHandler
            {
                UseProxy = false,
                UseCookies = true
            };
            clientHandler.ServerCertificateCustomValidationCallback += (sender, cert, chaun, ssl) => { return true; };
            cookieContainer = SettingHelper.UserInfoCookie.CreateCookieContainer();
            clientHandler.CookieContainer = cookieContainer;
            httpClient = new HttpClient(clientHandler) { Timeout = Timeout };
        }
        public CookieContainer GetCookies() => cookieContainer;
        public async Task<string> GetResults(IApiInfo apiInfo)
        {
            string url = apiInfo.ToString();
            return await GetResults(url);
        }
        public async Task<string> GetResults(string url, string host)
        {
            var before = httpClient.DefaultRequestHeaders.Host;
            try
            {
                httpClient.DefaultRequestHeaders.Host = host;
                var message = new HttpRequestMessage(HttpMethod.Get, url);
                message.Headers.Add("Cookie", SettingHelper.UserInfoCookie);
                var response = await httpClient.SendAsync(message).ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                var encodeResults = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
                return Encoding.UTF8.GetString(encodeResults, 0, encodeResults.Length);
            }
            finally
            {
                httpClient.DefaultRequestHeaders.Host = before;
            }
        }
        public async Task<string> GetResultsWithUrl(string url, ZipType zipType = ZipType.Normal)
        {
            try
            {
                var message = new HttpRequestMessage(HttpMethod.Get, url);
                message.Headers.Add("Cookie", SettingHelper.UserInfoCookie);
                var response = await httpClient.SendAsync(message).ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                switch (zipType)
                {
                    case ZipType.Normal:
                        {
                            var encodeResults = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
                            return Encoding.UTF8.GetString(encodeResults);
                        }
                    case ZipType.Deflate:
                        {
                            var encodeResults = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
                            using DeflateStream deflateStream = new DeflateStream(encodeResults, CompressionMode.Decompress);
                            using StreamReader streamReader = new StreamReader(deflateStream, Encoding.UTF8);
                            return streamReader.ReadToEnd();
                        }
                    case ZipType.GZip:
                        {
                            var encodeResults = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
                            using GZipStream zipStream = new GZipStream(encodeResults, CompressionMode.Decompress);
                            using StreamReader streamReader = new StreamReader(zipStream, Encoding.UTF8);
                            return streamReader.ReadToEnd();
                        }
                }
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetResultsWithUrl", ex);
            }
            return string.Empty;
        }
        public async Task<string> GetResults(string url)
        {
            try
            {
                HttpResponseMessage response = await httpClient.GetAsync(url).ConfigureAwait(false);
                response.EnsureSuccessStatusCode();
                var encodeResults = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
                return Encoding.UTF8.GetString(encodeResults, 0, encodeResults.Length);
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetResults", ex);
                return string.Empty;
            }
        }
        public async Task<HttpResponseMessage> GetRespone(string url)
        {
            try
            {
                return await httpClient.GetAsync(url).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetRespone", ex);
                return default;
            }
        }
        public async Task<(string Data, HttpResponseMessage Message)> GetResultsWithCookies(string url)
        {
            try
            {
                var response = await httpClient.GetAsync(url);
                response.EnsureSuccessStatusCode();
                var encodeResults = await response.Content.ReadAsByteArrayAsync();
                return (Encoding.UTF8.GetString(encodeResults, 0, encodeResults.Length), response);
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetResultsWithCookies", ex);
                return default;
            }
        }
        public async Task<string> PostResults(IApiInfo apiInfo, string postContent)
        {
            var url = apiInfo.ToString();
            return await PostResults(url, postContent);
        }
        public async Task<string> PostResults(string url, string postContent)
        {
            try
            {
                StringContent stringContent = new StringContent(postContent, Encoding.UTF8, "application/x-www-form-urlencoded");
                var response = await httpClient.PostAsync(url, stringContent);
                response.EnsureSuccessStatusCode();
                var encodeResults = await response.Content.ReadAsByteArrayAsync();
                return Encoding.UTF8.GetString(encodeResults, 0, encodeResults.Length);
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("PostResults", ex);
                return string.Empty;
            }
        }
        public async Task<(string Data, HttpResponseMessage Message)> PostResultsWithCookies(string url, string postContent)
        {
            try
            {
                StringContent stringContent = new StringContent(postContent, Encoding.UTF8, "application/x-www-form-urlencoded");
                var response = await httpClient.PostAsync(url, stringContent);
                response.EnsureSuccessStatusCode();
                var encodeResults = await response.Content.ReadAsByteArrayAsync();
                return (Encoding.UTF8.GetString(encodeResults, 0, encodeResults.Length), response);
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("PostResults", ex);
                return default;
            }
        }
        public async Task<string> PostResultsUtf8(IApiInfo apiInfo, string postContent)
        {
            var url = apiInfo.ToString();
            return await PostResultsUtf8(url, postContent);
        }
        public async Task<string> PostResultsUtf8(string url, string postContent)
        {
            try
            {
                StringContent stringContent = new StringContent(postContent, Encoding.UTF8, "application/x-www-form-urlencoded");
                var response = await httpClient.PostAsync(url, stringContent);
                response.EnsureSuccessStatusCode();
                var encodeResults = await response.Content.ReadAsByteArrayAsync();
                return Encoding.UTF8.GetString(encodeResults, 0, encodeResults.Length);
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("PostResultsUtf8", ex);
                return string.Empty;
            }
        }
        public async Task<string> PostResults(string url, string postContent, string referer)
        {
            try
            {
                httpClient.DefaultRequestHeaders.Referrer = new Uri(referer);
                StringContent stringContent = new StringContent(postContent, Encoding.UTF8, "application/x-www-form-urlencoded");
                var response = await httpClient.PostAsync(url, stringContent);
                response.EnsureSuccessStatusCode();
                return await response.Content.ReadAsStringAsync();
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("PostResults", ex);
                return string.Empty;
            }
            finally
            {
                httpClient.DefaultRequestHeaders.Referrer = new Uri(Current.HOST);
            }
        }
        public async Task<string> GetResultsGZip(string url)
        {
            try
            {
                HttpResponseMessage hr = await httpClient.GetAsync(url).ConfigureAwait(false);
                hr.EnsureSuccessStatusCode();
                using var encodeResults = await hr.Content.ReadAsStreamAsync().ConfigureAwait(false);
                using GZipStream zipStream = new GZipStream(encodeResults, CompressionMode.Decompress);
                using StreamReader streamReader = new StreamReader(zipStream, Encoding.UTF8);
                return streamReader.ReadToEnd();
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetResultsGZip", ex);
                return string.Empty;
            }
        }
        public async Task<string> GetResultsDeflate(string url)
        {
            try
            {
                HttpResponseMessage hr = await httpClient.GetAsync(url).ConfigureAwait(false);
                hr.EnsureSuccessStatusCode();
                using var encodeResults = await hr.Content.ReadAsStreamAsync().ConfigureAwait(false);
                using DeflateStream deflateStream = new DeflateStream(encodeResults, CompressionMode.Decompress);
                using StreamReader streamReader = new StreamReader(deflateStream, Encoding.UTF8);
                return streamReader.ReadToEnd();
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetResultsDeflate", ex);
                return string.Empty;
            }
        }
        public async Task<string> GetResultsUTF8Encode(IApiInfo apiInfo)
        {
            string url = apiInfo.ToString();
            return await GetResultsUTF8Encode(url);
        }
        public async Task<string> GetResultsUTF8Encode(string url)
        {
            try
            {
                HttpResponseMessage hr = await httpClient.GetAsync(url).ConfigureAwait(false);
                hr.EnsureSuccessStatusCode();
                var encodeResults = await hr.Content.ReadAsByteArrayAsync().ConfigureAwait(false);
                return Encoding.UTF8.GetString(encodeResults, 0, encodeResults.Length);
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetResultsUTF8Encode", ex);
                return string.Empty;
            }
        }
        public async Task GetImageStream(string url, Action<byte[]> callBack)
        {
            try
            {
                if (url.IsNotEmpty())
                {
                    var bytes = await httpClient.GetByteArrayAsync(new Uri(url)).ConfigureAwait(false);
                    callBack?.Invoke(bytes);
                }
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("GetImageStream", ex);
            }
        }
        public async Task<UploadImages> UploadImage(FileInfo fileInfo)
        {
            UploadImages model = default;
            try
            {
                ApiInfo apiInfo = new ApiInfo("http://api.vc.bilibili.com/api/v1/image/upload", "&src=bilih5", true);
                using var fileStream = fileInfo.OpenRead();
                var bytes = new byte[fileStream.Length];
                fileStream.Read(bytes, 0, bytes.Length);
                var multipartFormData = new MultipartFormDataContent { { new ByteArrayContent(bytes), "file_up", fileInfo.Name } };
                var responseMessage = await httpClient.PostAsync(apiInfo.ToString(), multipartFormData).ConfigureAwait(false);
                var content = await responseMessage.Content.ReadAsStringAsync();
                UploadImages uploadImagesModel = content.ParseObject<UploadImages>();
                if (uploadImagesModel.code == 0)
                {
                    uploadImagesModel.data.image_size = fileInfo.Length / 1024;
                    model = uploadImagesModel.data;
                }
            }
            catch (Exception ex)
            {
                LogManager.Instance.LogError("UploadImage", ex);
            }
            return model;
        }
        public void Dispose()
        {
            try
            {
                httpClient?.Dispose();
            }
            catch { }
        }
    }
}